layout.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import type React from "react"
  2. import { notFound } from "next/navigation"
  3. import { NextIntlClientProvider } from "next-intl"
  4. import { getMessages, getTranslations, setRequestLocale } from "next-intl/server"
  5. import type { Metadata, Viewport } from "next"
  6. import { Inter } from "next/font/google"
  7. import Providers from "@/components/providers"
  8. const inter = Inter({ subsets: ["latin"] })
  9. const locales = ["en", "zh"]
  10. export function generateStaticParams() {
  11. return locales.map((locale) => ({ locale }))
  12. }
  13. // 单独导出viewport配置
  14. export const viewport: Viewport = {
  15. width: 'device-width',
  16. initialScale: 1,
  17. maximumScale: 1,
  18. }
  19. // 动态生成 metadata
  20. export async function generateMetadata({
  21. params
  22. }: {
  23. params: Promise<{ locale: string }>
  24. }): Promise<Metadata> {
  25. const { locale } = await params
  26. // 获取翻译
  27. const t = await getTranslations({ locale, namespace: 'seo' })
  28. return {
  29. title: t('title'),
  30. description: t('description'),
  31. keywords: t('keywords'),
  32. authors: [{ name: 'Aiartools Team' }],
  33. creator: 'Aiartools',
  34. publisher: 'Aiartools',
  35. robots: {
  36. index: true,
  37. follow: true,
  38. googleBot: {
  39. index: true,
  40. follow: true,
  41. 'max-video-preview': -1,
  42. 'max-image-preview': 'large',
  43. 'max-snippet': -1,
  44. },
  45. },
  46. openGraph: {
  47. type: 'website',
  48. locale: locale,
  49. alternateLocale: locale === 'en' ? 'zh' : 'en',
  50. url: 'https://aiartools.com',
  51. title: t('ogTitle'),
  52. description: t('ogDescription'),
  53. siteName: 'Aiartools',
  54. images: [
  55. {
  56. url: '/images/og-image.png',
  57. width: 1200,
  58. height: 630,
  59. alt: t('ogTitle'),
  60. },
  61. ],
  62. },
  63. twitter: {
  64. card: 'summary_large_image',
  65. title: t('twitterTitle'),
  66. description: t('twitterDescription'),
  67. images: ['/images/og-image.png'],
  68. creator: '@aiartools',
  69. site: '@aiartools',
  70. },
  71. icons: {
  72. icon: "/images/favicon.ico",
  73. },
  74. manifest: '/site.webmanifest',
  75. alternates: {
  76. canonical: `https://aiartools.com/${locale}`,
  77. languages: {
  78. 'en': 'https://aiartools.com/en',
  79. 'zh': 'https://aiartools.com/zh',
  80. 'x-default': 'https://aiartools.com/en',
  81. },
  82. },
  83. }
  84. }
  85. export default async function LocaleLayout({
  86. children,
  87. params,
  88. }: {
  89. children: React.ReactNode
  90. params: Promise<{ locale: string }>
  91. }) {
  92. const { locale } = await params
  93. if (!locale || !locales.includes(locale)) {
  94. console.log(`Invalid locale received: ${locale}`)
  95. notFound()
  96. }
  97. // Enable static rendering
  98. setRequestLocale(locale);
  99. // Providing all messages to the client
  100. // side is the easiest way to get started
  101. const messages = await getMessages()
  102. return (
  103. <html lang={locale} suppressHydrationWarning>
  104. <head>
  105. <link rel="preconnect" href="https://fonts.googleapis.com" />
  106. <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
  107. <meta name="theme-color" content="#6366f1" />
  108. <meta name="color-scheme" content="light dark" />
  109. <script
  110. type="application/ld+json"
  111. dangerouslySetInnerHTML={{
  112. __html: JSON.stringify({
  113. "@context": "https://schema.org",
  114. "@type": "WebApplication",
  115. "name": "Aiartools",
  116. "description": locale === 'zh'
  117. ? "用AI的力量改变你的图像。简单、快速、功能强大。"
  118. : "Transform your images with the power of AI. Simple, fast, and incredibly powerful.",
  119. "url": "https://aiartools.com",
  120. "applicationCategory": "DesignApplication",
  121. "operatingSystem": "Web",
  122. "offers": {
  123. "@type": "Offer",
  124. "price": "0",
  125. "priceCurrency": "USD"
  126. },
  127. "aggregateRating": {
  128. "@type": "AggregateRating",
  129. "ratingValue": "4.8",
  130. "ratingCount": "1250"
  131. }
  132. })
  133. }}
  134. />
  135. </head>
  136. <body className={inter.className}>
  137. <Providers>
  138. <NextIntlClientProvider messages={messages}>
  139. {children}
  140. </NextIntlClientProvider>
  141. </Providers>
  142. </body>
  143. </html>
  144. )
  145. }